import { app, _timeout } from "../../experience/utils/F7.js";
import Component from "../Component.js";
import Type from "../../experience/db/Type.js";

const Point = ol.geom.Point;
const Fill = ol.style.Fill;
const Text = ol.style.Text;
const CircleStyle = ol.style.Circle;
const Feature = ol.Feature;
const Stroke = ol.style.Stroke;
const Style = ol.style.Style;
const LineString = ol.geom.LineString;

export default class Route extends Component{
    constructor() {
        super();

        this.THRESHOLD = 3;
        this.ISROUTE = false;
        this.locked = false;
        this.routes = [];
        this.cartrack = false;
        this.takeAtaxi = false;
        this.coordinates = [];

        // 开始点和结束点
        this.start = null;
        this.end = null;
        this.tracks = new Map();

        const config = this.algo.getConfig();
        if (config === null) return;

        // 轨迹线
        const param = { meta_id: config.meta_id, type: "vehicle" };
        this.api.getTracks(param).then(t => {
            t.data.forEach(track => {
                track.dis = 0;
                for (let i = 0; i < track.points.length - 1; i++) {
                    const sp = track.points[i], ep = track.points[i + 1];
                    track.dis += this.algo.distance(sp[0], sp[1], ep[0], ep[1]);
                }
                this.tracks.set(track.id, track);
            });
        });
    }

    clear() {
        this.drawer.source(Type.Modify).clear();
        this.routes = [];
        this.start = null;
        this.end = null;
        this.ISROUTE = false;
        this.locked = false;
    }

    startup(self) {
        self.toast("点击路径位置,选择开始点。");
        self.ISROUTE = true;

        // 在某些情况下需要临时让选择器失效
        this.emitter.emit("prevent-slector");
        window.onkeydown = function(event) {
            if (event.defaultPrevented) return;

            let handled = false;
            if (event.key === "Escape") {
                handled = true;
                self.emitter.emit("restore-slector");
                self.clear();
                self.toast("经路计算模式已关闭。");
                window.onkeydown = null;
            }

            if (handled) event.preventDefault();
        }
    }

    select(self, ev) {
        if (!self.ISROUTE || self.locked) return;

        const p = [0, 0];
        p[0] = self.algo.denormalX(ev.coordinate[0]);
        p[1] = self.algo.denormalY(ev.coordinate[1]);

        let flag = false;
        for (const t of self.tracks.values()) {
            if (self.algo.pointInBox(p[0], p[1], t.left, t.right, t.bottom, t.top)) {
                for (let i = 0; i < t.points.length - 1; i++) {
                    const f = self.algo.pointAtLine(p, t.points[i], t.points[i+1]);
                    if (f.flag) {
                        const d = self.algo.distance(f.foot[0], f.foot[1], p[0], p[1]);
                        if (d < self.THRESHOLD) {
                            if (self.start === null) {
                                self.start = { tid: t.id, index: i, xy: f.foot };
                                self.ending(self.utils.uuid(), p, f.foot);
                                self.toast("点击路径位置,选择结束点。");
                                flag = true;
                            } else {
                                self.end = { tid: t.id, index: i, xy: f.foot};
                                self.ending(self.utils.uuid(), p, f.foot, false);
                                self.locked = true;
                                flag = true;
                            }
                        }
                    }
                    if (flag) break;
                }
            }
            if (flag) break;
        }
    }

    distance(points) {
        let l = 0;
        for (let i = 0; i < points.length - 1; i++) {
            const sp = points[i], ep = points[i + 1];
            l += this.algo.distance(sp[0], sp[1], ep[0], ep[1]);
        }
        return l;
    }

    ending(id, pt, foot, flag=true) {
        const text = '\ue651', font = `normal 40px "iconfont"`;
        const green = new Fill({ color: "green" });
        const red = new Fill({ color: "red" });
        const color = flag ? "green" : "red";
        const fill = flag ? green : red;
        const style = new Style({ text: new Text({ text: text, fill: fill, font: font }) });
        const stroke = new Stroke({ width: 3, color: color, lineDash: [10, 10] });
        const circle = new CircleStyle({ radius: 5, fill: fill });

        // 旗子位置
        const positionFeature = new Feature(), p = [0, 0];
        positionFeature.setId(`postion_${id}`);
        p[0] = this.algo.normalX(pt[0]);
        p[1] = this.algo.normalY(pt[1]);
        positionFeature.setStyle(style);
        positionFeature.setGeometry(new Point(p));
        this.drawer.source(Type.Modify).addFeature(positionFeature);

        // 和垂足的连接线
        const lineFeature = new Feature(), f = [0, 0];
        lineFeature.setId(`line_${id}`);
        f[0] = this.algo.normalX(foot[0]);
        f[1] = this.algo.normalY(foot[1]);
        lineFeature.setStyle(new Style({ stroke: stroke }));
        lineFeature.setGeometry(new LineString([p, f]));
        this.drawer.source(Type.Modify).addFeature(lineFeature);

        // 垂足
        const circlefeature = new Feature();
        circlefeature.setStyle(new Style({ image: circle }));
        circlefeature.setGeometry(new Point(f));
        this.drawer.source(Type.Modify).addFeature(circlefeature);
    }

    same() {
        const result = { id: this.utils.uuid(), dir: 0, dis: 0, trask: [], points: [], fin: true };

        const track = this.tracks.get(this.start.tid);
        let start = 0, end = 0;
        if (this.start.index <= this.end.index) {
            start = this.start.index + 1; end = this.end.index;
            result.dir = 0;
        } else {
            start = this.end.index + 1; end = this.start.index;
            result.dir = 2;
        }

        result.points = [this.start.index <= this.end.index ? this.start.xy : this.end.xy];
        for (let i = start; i < end + 1; i++) {
            result.points.push(track.points[i]);
        }
        result.points.push(this.start.index > this.end.index ? this.start.xy : this.end.xy);

        this.routes.push(result);
    }

    depth(track) {
        // 已经没有可用的路径
        if (this.noused.size === 0) return;

        // 判断和结尾估计是否相能够拼接
        const tp = track.points[track.points.length - 1];
        const etrack = this.tracks.get(this.end.tid);
        const sp = etrack.points[0], ep = etrack.points[etrack.points.length - 1];

        // 和头部点进行比较,满足则直接返回
        const tsd = this.algo.distance(tp[0], tp[1], sp[0], sp[1]);
        if (tsd < this.algo.threshold) {
            for (let i = 1; i < this.end.index + 1; i++) {
                track.points.push(etrack.points[i]);
            }
            track.points.push(this.end.xy);
            track.dis = this.distance(track.points);
            track.fin = true;
            this.noused.clear();
            return;
        }

        // 和尾部点进行比较,满足则直接返回
        const ted = this.algo.distance(tp[0], tp[1], ep[0], ep[1]);
        if (ted < this.algo.threshold) {
            for (let i = etrack.points.length - 1; i > this.end.index; i--) {
                track.points.push(etrack.points[i]);
            }
            track.points.push(this.end.xy);
            track.dis = this.distance(etrack.points);
            track.fin = true;
            this.noused.clear();
            return;
        }

        // 如果不能拼接,则将没有用到的轨迹加入到数组中
        for (const noused of this.noused.values()) {
            const tp = track.points[track.points.length - 1];
            const sp = noused.points[0], ep = noused.points[noused.points.length - 1];
            const used = JSON.parse(JSON.stringify(track.used));
            const points = JSON.parse(JSON.stringify(track.points));

            // 当前轨迹的尾部与需要加入轨迹的开头相同
            const tsd = this.algo.distance(tp[0], tp[1], sp[0], sp[1]);
            if (tsd < this.algo.threshold) {
                used.push(noused.id);
                for (let i = 1; i < noused.points.length; i++) {
                    points.push(noused.points[i]);
                }
                const nt = {
                    id: this.utils.uuid(),
                    dir: track.dir,
                    dis: this.distance(points),
                    used: used,
                    points: points,
                    fin: false
                };
                this.routes.push(nt);
                this.noused.delete(noused.id);
                this.depth(nt);
            }

            // 当前轨迹的尾部与需要加入轨迹的结尾相同
            const ted = this.algo.distance(tp[0], tp[1], ep[0], ep[1]);
            if (ted < this.algo.threshold) {
                used.push(noused.id);
                for (let i = noused.points.length - 2; i >= 0; i--) {
                    points.push(noused.points[i]);
                }
                const nt = {
                    id: this.utils.uuid(),
                    dir: track.dir,
                    dis: this.distance(points),
                    used: used,
                    points: points,
                    fin: false
                };
                this.routes.push(nt);
                this.noused.delete(noused.id);
                this.depth(nt);
            }
        }
    }

    draw() {
        // 去掉没有找到节点点路径的路径,进行排序找出最短的路径
        const routes = this.routes.filter((x) => x.fin);
        routes.sort((a, b) => a.dis - b.dis);
        if (routes.length === 0) {
            this.toast("未查找到任何一条路径。");
            return;
        }

        // 这里特殊判断一下
        this.coordinates = routes[0].points;
        if (this.coordinates.length === 0) {
            this.toast("未查找到任何一条路径。");
            return;
        }

        // 使用最短路径进行描画
        const points = JSON.parse(JSON.stringify(routes[0].points));
        points.forEach(p => {
            p[0] = this.algo.normalX(p[0]);
            p[1] = this.algo.normalY(p[1]);
        });

        const feature = new Feature();
        const stroke = new Stroke({ width: 3, color: "green"});
        feature.setId(routes[0].id);
        feature.setStyle(new Style({ stroke: stroke }));
        feature.setGeometry(new LineString(points));
        this.drawer.source(Type.Modify).addFeature(feature);
    }

    route() {
        app.dialog.preloader("路线计算中,请耐心等待...");

        // 如果开始点和结束点在同一条轨迹线上
        if (this.start.tid === this.end.tid) {
            this.same();
            this.draw();
            app.dialog.close();
            return;
        }

        // 首先将开始点和结束点轨迹之外的轨迹线设置标记
        this.noused = new Map();
        for (const track of this.tracks.values()) {
            if (track.id === this.start.tid || track.id === this.end.tid) {
                continue;
            }
            this.noused.set(track.id, track);
        }

        // 找到开始点所在轨迹线
        const strack = this.tracks.get(this.start.tid);
        const fid = this.utils.uuid(), rid = this.utils.uuid();

        // 设置正向数据
        const forward = { id: fid, dir: 0, dis: 0, used: [], points: [this.start.xy], fin: false };
        this.routes.push(forward);
        for (let i = this.start.index + 1; i < strack.points.length; i++) {
            forward.points.push(strack.points[i]);
        }
        forward.dis = this.distance(forward.points);
        this.depth(forward);

        // 设置反向数据
        const reverse = { id: rid, dir: 2, dis: 0, used: [], points: [this.start.xy], fin: false };
        this.routes.push(reverse);
        for (let i = this.start.index; i >= 0; i--) {
            reverse.points.push(strack.points[i]);
        }
        reverse.dis = this.distance(reverse.points);
        this.depth(reverse);

        // 结果描画
        this.draw();

        app.dialog.close();
    }

    async trackref() {
        // 给个默认时间,年初到年尾
        const stime = moment().startOf('year').format('YYYY-MM-DDT00:00');
        const etime = moment().add(1, 'years').startOf('year').format('YYYY-MM-DDT00:00');
        this.$$("#car-track-stime").val(stime);
        this.$$("#car-track-etime").val(etime);

        // 添加表格数据
        const data = await this.api.getCars();
        this.$$("#car-track-table").empty();
        for (let i = 0; i < data.data.length; i++) {
            const item = data.data[i], id = `cartrack---${item.carcode}---${item.name}`;
            let tr= `
                <tr id="${id}">
                    <td class="label-cell" style="width: 100px;">车辆编码</td>
                    <td class="label-cell" >${item.carcode}</td>
                    <td class="label-cell" style="width: 100px;">车辆名称</td>
                    <td class="label-cell">${item.name}</td>
                </tr>
            `;
            this.$$("#car-track-table").append(this.$$(tr));

            this.$$('#' + id).on("click", async () => {
                this.app.dialog.confirm(`是否将数据提交到服务器?`, async () => {
                    const stime = this.$$("#car-track-stime").val();
                    const etime = this.$$("#car-track-etime").val();
                    const car_id = id.split("---")[1];
                    const car_name = id.split("---")[2];

                    const params = {
                        car_id: car_id,
                        car_name: car_name,
                        start_time: stime,
                        end_time: etime,
                        coordinates: this.coordinates
                    };
                    console.log(params);

                    const data = await this.api.addCarTrack(params);
                    if (data.code === 0) {
                        this.toast("数据提交成功!");
                    } else {
                        this.toast("数据失败成功!");
                    }

                    this.clear();
                    this.cartrack = false;
                    this.emitter.emit("restore-slector");
                    this.popup.close();
                });
            });
        }
    }

    async tracklst() {
        // 添加表格数据
        const data = await this.api.getCarTracks();
        this.$$("#car-track-list-table").empty();
        this.coordinatesMap = new Map();
        for (let i = 0; i < data.data.length; i++) {
            const item = data.data[i], coordinates = item.coordinates;
            const id = `car-track-list-${item.id}-${item.track_id}-${item.car_id}`;
            this.coordinatesMap.set(id, coordinates);
            let tr= `
                    <tr id="${id}" style="cursor: pointer;">
                        <td style="width: 100px; padding: 5px;">车辆编码<br>${item.car_id}</td>
                        <td style="width: 100px; padding: 5px;">车辆名称<br>${item.car_name}</td>
                        <td style="width: 160px; padding: 5px;">开始时间<br>${item.start_time}</td>
                        <td style="width: 160px; padding: 5px;">结束时间<br>${item.end_time}</td>
                    </tr>
                `;
            this.$$("#car-track-list-table").append(this.$$(tr));

            this.$$('#' + id).on("click", async () => {
                const dialog = this.app.dialog.create({
                    title: "提示",
                    text: "请选择需要进行的操作？",
                    buttons: [
                        {
                            text: "显示轨迹线位置",
                            onClick: () => {
                                const coordinates = this.coordinatesMap.get(id)
                                const feature = new Feature();
                                coordinates.forEach((point) => {
                                    point[0] = this.algo.normalX(point[0]);
                                    point[1] = this.algo.normalY(point[1]);
                                })

                                this.drawer.map.getView().setCenter([coordinates[0][0], coordinates[0][1]]);

                                const stroke = new Stroke({ width: 5, color: "GreenYellow" });
                                feature.setStyle(new Style({ stroke: stroke }));
                                feature.setGeometry(new LineString(coordinates));
                                this.drawer.source(Type.Modify).addFeature(feature);
                                this.listpopup.close();
                                dialog.close();

                                const self = this;
                                this.toast("图上绿色为限定范围,点击'ESC'清除。");
                                window.onkeydown = function(event) {
                                    if (event.defaultPrevented) return;
                                    let handled = false;
                                    if (event.key === "Escape") {
                                        handled = true;
                                        self.drawer.source(Type.Modify).clear();
                                    }
                                    if (handled) event.preventDefault();
                                }
                            }
                        },
                        {
                            text: "删除记录",
                            onClick: async () => {
                                const did = id.split("-")[3];
                                const data = await this.api.delCarTrack(did);
                                if (data.code === 0) {
                                    this.toast("数据删除成功!");
                                    await this.tracklst();
                                } else {
                                    this.toast("数据删除失败!");
                                }
                                dialog.close();
                            }
                        },
                        {
                            text: "取消",
                            onClick: async () => {
                                dialog.close();
                            }
                        },
                    ],
                    verticalButtons: true,
                }).open();
            });
        }
    }

    template() {
        const div = `
            <div class="popup car-track-popup">
                <header>关联车辆</header>
                <article>
                    <div class="list inline-labels no-hairlines-md">
                        <ul>
                            <li class="item-content item-input">
                                <div class="item-title item-label" style="width: 80px;">开始时间:</div>
                                <div class="item-inner">
                                    <div class="item-input-wrap">
                                        <label for="car-track-stime">
                                            <input type="datetime-local" id="car-track-stime">
                                        </label>
                                    </div>
                                </div>
                                <div class="item-title item-label" style="width: 80px; margin-left: 40px;">
                                    结束时间:</div>
                                <div class="item-inner">
                                    <div class="item-input-wrap">
                                        <label for="car-track-etime">
                                            <input type="datetime-local" id="car-track-etime">
                                        </label>
                                    </div>
                                </div>
                            </li>
                        </ul>
                        <div class="card data-table" style="height: 520px; margin-top: 10px">
                            <table border="1" class="table">
                                <tbody id="car-track-table"></tbody>
                            </table>
                        </div>
                    </div>
                </article>
                <a href="#" class="popup-closer" id="car-track-close"></a>
            </div>
        `;
        this.$$("#article").append(div);

        const div2 = `
            <div class="popup car-track-list-popup">
                <header>车辆关联列表</header>
                <article>
                    <div class="list inline-labels no-hairlines-md">
                        <div class="card data-table" style="height: 560px; margin-top: 20px">
                            <table border="1" class="table">
                                <tbody id="car-track-list-table"></tbody>
                            </table>
                        </div>
                    </div>
                </article>
                <a href="#" class="popup-closer" id="car-track-list-close"></a>
            </div>
        `;
        this.$$("#article").append(div2);
    }

    control() {
        const self = this;

        self.popup = this.app.popup.create({ el: '.car-track-popup' });
        self.listpopup = this.app.popup.create({ el: '.car-track-list-popup' });

        self.$$("#car-track-close").on("click", () => {
            self.popup.close();
        });

        self.$$("#car-track-list").on("click", async () => {
            self.toast("点击列表进行查看或删除。");
            await self.tracklst();
            self.listpopup.open();
        });

        self.$$("#car-track-list-close").on("click", () => {
            self.listpopup.close();
        });

        // 打开路线计算
        self.$$("#route-calc").on("click", () => {
            self.startup(self);
        });

        // 打开路线计算
        self.$$("#car-track").on("click", () => {
            self.startup(self);
            self.cartrack = true;
        });

        self.emitter.on("take-a-taxi", () => {
            self.takeAtaxi = true;
        });

        // 给整个地图添加点击事件
        self.drawer.map.on("click", async (ev) => {
            // 点击地图选中目标
            self.select(self, ev);

            // 判断点击是否结束,进行路线计算
            if (self.start !== null && self.end !== null) {
                self.route();

                // 如果计算出来的路线坐标为空
                if (self.coordinates.length === 0) {
                    self.emitter.emit("restore-slector");
                    self.clear();
                    return;
                }

                // 弹出关联对话框
                if (self.cartrack) {
                    self.toast("点击列表选择需要关联的车辆。");
                    await self.trackref();
                    self.popup.open();
                }

                // 打车
                if (self.takeAtaxi) {
                    const route = { start: self.start,  end: self.end, xys: self.coordinates };
                    window.parent.postMessage(JSON.stringify(route), '*');
                }
            }
        });
    }
}